package org.springframework.security.captcha;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.web.filter.GenericFilterBean;

public class ValidateCodeAuthenticationFilter extends GenericFilterBean
{
  private String validateCodeId;
  public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_captcha";
  public static final String SPRING_SECURITY_DEFAULT_PROCESSES_URL = "/j_spring_security_check";
  private boolean postOnly;
  private String filterProcessesUrl;
  private String authenticationFailureUrl;
  private AuthenticationFailureHandler failureHandler;

  public ValidateCodeAuthenticationFilter()
  {
    this.postOnly = true;
    this.filterProcessesUrl = "/j_spring_security_check";
    this.failureHandler = new SimpleUrlAuthenticationFailureHandler();
  }

  private boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response)
  {
    String uri = request.getRequestURI();
    int pathParamIndex = uri.indexOf(';');
    if (pathParamIndex > 0)
      uri = uri.substring(0, pathParamIndex);
    if ("".equals(request.getContextPath())) {
      return uri.endsWith(getFilterProcessesUrl());
    }
    return uri.endsWith(request
      .getContextPath() + getFilterProcessesUrl());
  }

  protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed)
    throws IOException, ServletException
  {
    if (this.logger.isDebugEnabled()) {
      this.logger.debug("Authentication request failed: " + 
        failed.toString());
      this.logger.debug("Updated SecurityContextHolder to contain null Authentication");
      this.logger.debug(
        "Delegating to authentication failure handler" + 
        this.failureHandler);
    }
    this.failureHandler.onAuthenticationFailure(request, response, failed);
  }

  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException
  {
    HttpServletRequest request = (HttpServletRequest)req;
    HttpServletResponse response = (HttpServletResponse)res;
    if (!requiresAuthentication(request, response)) {
      chain.doFilter(request, response);
      return;
    }
    attempAuthentication(request, response, chain);
  }

  private void attempAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
    throws IOException, ServletException
  {
    if ((this.postOnly) && (!request.getMethod().equals("POST"))) {
      redirectToFailureUrl(request, response);
      return;
    }
    try {
      if ((request.getParameter(this.validateCodeId) == null) || 
        (!request.getParameter(this.validateCodeId).equals(
        request.getSession().getAttribute(this.validateCodeId)))) {
        redirectToFailureUrl(request, response);
        return;
      }
    } catch (Exception e) {
      redirectToFailureUrl(request, response);
      return;
    }
    chain.doFilter(request, response);
  }

  private void redirectToFailureUrl(HttpServletRequest request, HttpServletResponse response) throws IOException
  {
    response.sendRedirect(request
      .getContextPath() + getAuthenticationFailureUrl());
  }

  public String getFilterProcessesUrl()
  {
    return this.filterProcessesUrl;
  }

  public void setFilterProcessesUrl(String filterProcessesUrl) {
    this.filterProcessesUrl = filterProcessesUrl;
  }

  public String getAuthenticationFailureUrl() {
    return this.authenticationFailureUrl;
  }

  public void setAuthenticationFailureUrl(String authenticationFailureUrl) {
    this.authenticationFailureUrl = authenticationFailureUrl;
  }

  public void setValidateCodeId(String validateCodeId) {
    this.validateCodeId = validateCodeId;
  }

  public String getValidateCodeId() {
    return this.validateCodeId;
  }
}